package com.example.networklab.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.networklab.common.BizException;
import com.example.networklab.common.ExceptionEnum;
import com.example.networklab.constants.MQConstants;
import com.example.networklab.constants.RedisConstants;
import com.example.networklab.domain.dto.PageDTO;
import com.example.networklab.domain.dto.TaskStatistics;
import com.example.networklab.domain.po.Task;
import com.example.networklab.domain.po.TaskHistory;
import com.example.networklab.domain.po.User;
import com.example.networklab.domain.query.PageQuery;
import com.example.networklab.domain.query.TeaTaskRecordPageQuery;
import com.example.networklab.domain.req.SubmitTaskReq;
import com.example.networklab.domain.vo.*;
import com.example.networklab.enums.TaskHistoryStatus;
import com.example.networklab.enums.TaskStatus;
import com.example.networklab.mapper.TaskHistoryMapper;
import com.example.networklab.mapper.TaskMapper;
import com.example.networklab.mq.dto.TaskCheckMQDTO;
import com.example.networklab.service.ITaskHistoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.networklab.service.IUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 作业提交记录表 服务实现类
 * </p>
 *
 * @author dzw
 * @since 2025-02-27
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TaskHistoryServiceImpl extends ServiceImpl<TaskHistoryMapper, TaskHistory> implements ITaskHistoryService {
    private final IUserService userService;
    private final TaskMapper taskMapper;
    private final RabbitTemplate rabbitTemplate;
    private final RedissonClient redissonClient;
    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public List<TaskStatistics> getTaskStatistics(List<Task> taskList) {
        ArrayList<TaskStatistics> res = new ArrayList<>(taskList.size());
        for (Task task : taskList) {
            TaskStatistics statistic = new TaskStatistics();
            statistic.setTaskId(task.getId());
            statistic.setSubmitCount(this.baseMapper.getSubmitCount(task.getId()));
            statistic.setStudentSubmitCount(this.baseMapper.getStudentSubmitCount(task.getId()));
            statistic.setStudentCount(userService.countStudentsInClass(task.getGrade(), task.getClassNum()));
            res.add(statistic);
        }
        return res;
    }

    @Override
    public PageDTO<TeaSubmitHistoryVO> teaGetTaskRecord(Long taskId, TeaTaskRecordPageQuery pageQuery) {
        User user = null;
        if (StrUtil.isNotEmpty(pageQuery.getUsername())) {
            user = userService.lambdaQuery()
                    .eq(User::getUsername, pageQuery.getUsername())
                    .one();
        }
        Page<TaskHistory> page;
        if (user != null) {
            page = this.lambdaQuery()
                    .eq(TaskHistory::getTaskId, taskId)
                    .eq(TaskHistory::getUserId, user.getUserId())
                    .page(pageQuery.toMpPageDefaultSortByCreateTimeDesc());
        } else {
            page = this.lambdaQuery()
                    .eq(TaskHistory::getTaskId, taskId)
                    .page(pageQuery.toMpPageDefaultSortByCreateTimeDesc());
        }
        if (CollUtil.isEmpty(page.getRecords())) {
            return PageDTO.empty(page.getTotal(), page.getPages());
        }
        List<TaskHistory> taskHistories = page.getRecords();
        List<Long> userIds = taskHistories.stream().map(TaskHistory::getUserId).collect(Collectors.toList());
        Map<Long, User> map = userService.listByIds(userIds).stream().collect(Collectors.toMap(User::getUserId, i -> i));
        ArrayList<TeaSubmitHistoryVO> res = new ArrayList<>();
        for (TaskHistory taskHistory : taskHistories) {
            TeaSubmitHistoryVO vo = new TeaSubmitHistoryVO();
            BeanUtil.copyProperties(taskHistory, vo);
            // 差name和username
            User u = map.get(taskHistory.getUserId());
            if (u != null) {
                vo.setName(u.getName());
                vo.setUsername(u.getUsername());
            }
            res.add(vo);
        }
        return PageDTO.of(page, res);
    }

    @Override
    public void submitTask(SubmitTaskReq submitTaskReq) {
        // 对该接口进行限流和黑名单机制实现
        long userId = StpUtil.getLoginIdAsLong();
        String key = RedisConstants.BLACKLIST_PREFIX + userId;
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))) {
            throw new BizException(ExceptionEnum.TASK_SUBMIT_TOO_FREQUENT);
        }
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(String.valueOf(userId));
        rateLimiter.trySetRate(RateType.OVERALL, 10, 60, RateIntervalUnit.SECONDS);
        if (!rateLimiter.tryAcquire()) {
            stringRedisTemplate.opsForValue().set(key, String.valueOf(1), 5, TimeUnit.MINUTES);
            throw new BizException(ExceptionEnum.TASK_SUBMIT_TOO_FREQUENT);
        }
        Task task = taskMapper.selectById(submitTaskReq.getTaskId());
        LocalDateTime now = LocalDateTime.now();
        if (now.isBefore(task.getStartTime()) || now.isAfter(task.getEndTime())) {
            // 放入黑名单
            stringRedisTemplate.opsForValue().set(key, String.valueOf(1), 5, TimeUnit.MINUTES);
            throw new BizException(ExceptionEnum.TASK_NOT_IN_TIME);
        }
        TaskHistory taskHistory = new TaskHistory();
        taskHistory.setUserId(userId);
        taskHistory.setTaskId(submitTaskReq.getTaskId());
        taskHistory.setStatus(TaskHistoryStatus.NOT_CHECK);
        taskHistory.setConfig(submitTaskReq.getConfig());
        this.save(taskHistory);
        //发送消息给消息队列，使用workqueue模式，可以多个消费者一起消费
        TaskCheckMQDTO taskCheckMQDTO = new TaskCheckMQDTO();
        taskCheckMQDTO.setId(taskHistory.getId());
        taskCheckMQDTO.setConfig(taskHistory.getConfig());
        taskCheckMQDTO.setTopologyId(task.getTopologyId());
        log.info("发送消息queueName:[{}],消息内容:[{}]", MQConstants.TASK_CHECK_QUEUE_NAME, taskCheckMQDTO);
        rabbitTemplate.convertAndSend(MQConstants.TASK_CHECK_QUEUE_NAME, taskCheckMQDTO);
    }

    @Override
    public PageDTO<StuSubmitHistoryVO> stuGetTaskHistory(Long taskId, PageQuery pageQuery) {
        long userId = StpUtil.getLoginIdAsLong();
        Page<TaskHistory> page = this.lambdaQuery()
                .eq(TaskHistory::getTaskId, taskId)
                .eq(TaskHistory::getUserId, userId)
                .page(pageQuery.toMpPageDefaultSortByCreateTimeDesc());
        if (CollUtil.isEmpty(page.getRecords())) {
            return PageDTO.empty(page.getTotal(), page.getPages());
        }
        List<TaskHistory> taskHistories = page.getRecords();
        ArrayList<StuSubmitHistoryVO> res = new ArrayList<>();
        for (TaskHistory taskHistory : taskHistories) {
            StuSubmitHistoryVO vo = new StuSubmitHistoryVO();
            BeanUtil.copyProperties(taskHistory, vo);
            res.add(vo);
        }
        return PageDTO.of(page, res);
    }

    @Override
    public ConfigVO getTaskHistoryConfig(Long taskHistoryId) {
        TaskHistory taskHistory = this.getById(taskHistoryId);
        ConfigVO configVO = new ConfigVO();
        configVO.setConfig(taskHistory.getConfig());
        return configVO;
    }

    @Override
    public MaxScoreVO getTaskGrade(Long taskId) {
        long userId = StpUtil.getLoginIdAsLong();
        TaskHistory taskHistory = this.lambdaQuery()
                .eq(TaskHistory::getTaskId, taskId)
                .eq(TaskHistory::getUserId, userId)
                .eq(TaskHistory::getStatus, TaskHistoryStatus.CHECKED)
                .orderByDesc(TaskHistory::getScore)
                .last("limit 1")
                .one();
        if (taskHistory == null) {
            throw new BizException(ExceptionEnum.NO_GRADE);
        }
        MaxScoreVO maxScoreVO = new MaxScoreVO();
        maxScoreVO.setScore(taskHistory.getScore());
        return maxScoreVO;
    }
}
